package org.light.verb;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.light.core.Verb;
import org.light.core.Writeable;
import org.light.domain.Domain;
import org.light.domain.Dropdown;
import org.light.domain.Field;
import org.light.domain.JavascriptBlock;
import org.light.domain.JavascriptMethod;
import org.light.domain.Method;
import org.light.domain.Signature;
import org.light.domain.Statement;
import org.light.domain.Type;
import org.light.easyui.EasyUIPositions;
import org.light.exception.ValidateException;
import org.light.generator.NamedStatementGenerator;
import org.light.limitedverb.CountSearchByFieldsRecords;
import org.light.utils.DomainTokenUtil;
import org.light.utils.InterVarUtil;
import org.light.utils.StringUtil;
import org.light.utils.TableStringUtil;
import org.light.utils.WriteableUtil;

public class SearchByFields extends Verb implements EasyUIPositions {
	protected CountSearchByFieldsRecords countSearch = new CountSearchByFieldsRecords();

	@Override
	public Method generateDaoImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search"+this.domain.getPlural()+"ByFields");	
			method.setReturnType(new Type("List",this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport(this.domain.getPackageToken()+"."+this.domain.getDomainSuffix()+"."+this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1,"connection",new Type("Connection","java.sql"),"java.sql"));
			method.addSignature(new Signature(2,this.domain.getLowerFirstDomainName(),this.domain.getType(),this.domain.getPackageToken()));
			if (this.domain.containsDateTime()) {
				method.addAdditionalImport(this.domain.getPackageToken()+".utils.DateUtil");
			}

			List<Writeable> list = new ArrayList<Writeable>();
			list.add(new Statement(100,2,"try {"));
			list.add(new Statement(400L,2,"String query = \" select "+ DomainTokenUtil.generateTableCommaFields(domain) + " from "+ domain.getDbPrefix() + TableStringUtil.domainNametoTableName(domain)+" where 1=1 \";"));
			
			long serial = 500L;
			Set<Field> fields = this.domain.getSearchFields();
			for (Field f:fields){
				if (!this.deniedFields.contains(f)) {
					if (f.getFieldType().equalsIgnoreCase("string")){
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+"!=null && !\"\".equals("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+")){"));
						list.add(new Statement(serial+100,3,"query += \" and "+DomainTokenUtil.changeDomainFieldtoTableColum(f.getFieldName())+" like ? \";"));
					} else if (f.getFieldType().equalsIgnoreCase("date") || f.getFieldType().equalsIgnoreCase("datetime")){
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+" != null){"));
						list.add(new Statement(serial+100,3,"query += \" and date("+DomainTokenUtil.changeDomainFieldtoTableColum(f.getFieldName())+") = date(?) \";"));
					} else {
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+"!=null ){"));
						list.add(new Statement(serial+100,3,"query += \" and "+DomainTokenUtil.changeDomainFieldtoTableColum(f.getFieldName())+" = ? \";"));
					}
					list.add(new Statement(serial+200,2,"}"));
					serial += 300L;
				}
			}
			list.add(new Statement(serial+300,2,"Integer serial = 1;"));
			list.add(new Statement(serial+400,2,"PreparedStatement ps = connection.prepareStatement(query);"));
			list.add(new Statement(serial+500,2,""));
			
			serial += 600L;

			for (Field f:fields){
				if (!this.deniedFields.contains(f)) {
					if (f.getFieldType().equalsIgnoreCase("string")){
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+" != null && !\"\".equals("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+")){"));
						list.add(new Statement(serial+100,3,"ps.setString(serial++,\"%\"+"+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+"+\"%\");"));
					} else if (f.getFieldType().equalsIgnoreCase("date") || f.getFieldType().equalsIgnoreCase("datetime")){
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+" != null){"));
						list.add(new Statement(serial+100,3,"ps.setDate(serial++, DateUtil.utilDateToSqlDate("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+"));"));
					} else{
						list.add(new Statement(serial,2,"if ("+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+" != null){"));
						list.add(new Statement(serial+100,3,"ps.set"+StringUtil.capFirst(f.getPrimaryType().getTypeName())+"(serial++,"+this.domain.getLowerFirstDomainName()+"."+f.getGetterCall()+");"));
					}
					list.add(new Statement(serial+200,2,"}"));
					serial += 300L;
				}
			}

			list.add(new Statement(serial+200,2,""));
			list.add(new Statement(serial+300,2,"ResultSet result = ps.executeQuery();"));
			list.add(new Statement(serial+400,2,"ArrayList<"+this.domain.getCapFirstDomainNameWithSuffix()+"> list = new ArrayList<"+this.domain.getCapFirstDomainNameWithSuffix()+">();"));
			list.add(new Statement(serial+500,2,"while(result.next()) {"));
			list.add(new Statement(serial+600,3,"//Build the list object."));
			list.add(new Statement(serial+700,3,this.domain.getCapFirstDomainNameWithSuffix()+" " + this.domain.getLowerFirstDomainName()+"2 = new "+this.domain.getCapFirstDomainNameWithSuffix()+"();"));

			serial += 800;
			Set<Field> fields2 = this.domain.getFields();
			for (Field f:fields2){
				if (!this.deniedFields.contains(f)) {
					if ("date".equalsIgnoreCase(f.getFieldType())){
						list.add(new Statement(serial,3,StringUtil.lowerFirst(domain.getStandardName())+"2.set"+StringUtil.capFirst(f.getFieldName())+"(DateUtil.sqlDateToUtilDate("+InterVarUtil.DB.result.getVarName()+"."+Type.getPrimeTypeGetterName(f.getFieldType())+"(\""+f.getFieldTableColumName()+"\")));"));
					} else if ("datetime".equalsIgnoreCase(f.getFieldType())){
						list.add(new Statement(serial,3,StringUtil.lowerFirst(domain.getStandardName())+"2.set"+StringUtil.capFirst(f.getFieldName())+"(DateUtil.strDateTimeToUtilDateTime("+InterVarUtil.DB.result.getVarName()+"."+Type.getPrimeTypeGetterName(f.getFieldType())+"(\""+f.getFieldTableColumName()+"\")));"));
					} else {
						list.add(new Statement(serial,3,StringUtil.lowerFirst(domain.getStandardName())+"2.set"+StringUtil.capFirst(f.getFieldName())+"("+InterVarUtil.DB.result.getVarName()+"."+Type.getPrimeTypeGetterName(f.getFieldType())+"(\""+f.getFieldTableColumName()+"\"));"));
					}
					serial += 100L;
				}
			}
			list.add(new Statement(serial,3,"//Build the object list."));
			list.add(new Statement(serial+100,3,"list.add("+this.domain.getLowerFirstDomainName()+"2);"));
			list.add(new Statement(serial+200,2,"}"));
			list.add(new Statement(serial+300,2,"return list;"));
			list.add(new Statement(serial+400,2,"} catch (Exception e){"));
			list.add(new Statement(serial+500,3,"e.printStackTrace();"));
			list.add(new Statement(serial+600,3,"return null;"));
			list.add(new Statement(serial+700,2,"}"));
					
			method.setMethodStatementList(WriteableUtil.merge(list));
			return method;
		}
	}

	@Override
	public Method generateDaoMethodDefinition() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1,"connection",new Type("Connection","java.sql"),"java.sql"));
			method.addSignature(new Signature(2, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);

			return method;
		}
	}

	@Override
	public Method generateServiceMethodDefinition() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);

			return method;
		}
	}

	@Override
	public Method generateServiceImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDaoSuffix() + "."
					+ this.domain.getStandardName() + "Dao");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getServiceSuffix() + "."
					+ this.domain.getStandardName() + "Service");
			method.addMetaData("Override");

			List<Writeable> list = new ArrayList<Writeable>();
			Method daomethod = this.generateDaoMethodDefinition();
			list.add(NamedStatementGenerator.getDBConfInitDBAutoRelease(100L, 2,InterVarUtil.DB.connection, InterVarUtil.DB.dbconf));
			list.add(new Statement(200L,3, "return " + daomethod.generateStandardServiceImplCallString(InterVarUtil.DB.dao.getVarName())+";"));
			list.add(NamedStatementGenerator.getLoopFooter(300L, 2));
			method.setMethodStatementList(WriteableUtil.merge(list));

			return method;
		}
	}

	public SearchByFields() {
		super();
		this.dbType = "MariaDB";
		this.setLabel("按字段搜索");
	}
	
	public SearchByFields(String dbType) {
		super();
		this.dbType = dbType;
		this.setLabel("按字段搜索");
	}

	public SearchByFields(Domain domain) throws ValidateException{
		super();
		this.domain = domain;
		this.dbType = "MariaDB";
		this.denied = domain.isVerbDenied("SearchByFields");
		this.countSearch = new CountSearchByFieldsRecords(domain);
		this.setVerbName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
		this.setLabel("按字段搜索");
		if  (domain.getLanguage().equalsIgnoreCase("english"))  this.setLabel("SearchByFields");
	}
	
	public SearchByFields(Domain domain,String dbType) throws ValidateException{
		super();
		this.domain = domain;
		this.dbType = dbType;
		this.denied = domain.isVerbDenied("SearchByFields");
		this.countSearch = new CountSearchByFieldsRecords(domain);
		this.setVerbName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
		this.setLabel("按字段搜索");
		if  (domain.getLanguage().equalsIgnoreCase("english"))  this.setLabel("SearchByFields");
	}

	public SearchByFields(Domain domain,Set<Field> deniedFields) throws ValidateException{
		super();
		this.domain = domain;
		this.dbType = domain.getDbType();
		this.deniedFields = deniedFields;
		this.denied = domain.isVerbDenied("SearchByFields");
		this.countSearch = new CountSearchByFieldsRecords(domain);
		this.setVerbName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
		this.setLabel("按字段搜索");
		if  (domain.getLanguage().equalsIgnoreCase("english"))  this.setLabel("SearchByFields");
	}

	@Override
	public JavascriptBlock generateEasyUIJSButtonBlock() throws Exception {
		return null;
	}

	@Override
	public JavascriptMethod generateEasyUIJSActionMethod() throws Exception {
		return null;
	}

	@Override
	public Method generateDummyDaoImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + ".utils.StringUtil");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);

			List<Writeable> sList = new ArrayList<Writeable>();
			long serial = 1000L;			
			sList.add(new Statement(serial+1000L,2,"List<"+this.domain.getCapFirstDomainNameWithSuffix()+"> result = new ArrayList<>();"));
			sList.add(new Statement(serial+2000L,2,"for ("+this.domain.getCapFirstDomainNameWithSuffix()+" raw"+this.domain.getLowerFirstDomainName()+" : db){"));
			sList.add(new Statement(serial+3000L,3,"boolean matches = true;"));
			serial += 4000L;
			for (Field f: this.domain.getSearchFields()) {
				if (! deniedFields.contains(f)) {
					if (f instanceof Dropdown) {
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (!raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"().equals("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("String".equalsIgnoreCase(f.getFieldType())){					
						sList.add(new Statement(serial,3,"if (!StringUtil.isBlank("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) {"));
						sList.add(new Statement(serial+1000L,4,"if (!raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"().contains("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("int".equals(f.getFieldType())||"Integer".equals(f.getFieldType())||"long".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (!raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"().equals("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("float".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (Math.abs(raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() - "+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"()) > 0.0001) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("float".equalsIgnoreCase(f.getFieldType())||"double".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (Math.abs(raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() - "+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"()) > 0.0001) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("boolean".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (!raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"().equals("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("datetime".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+500L,4,"SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");"));
						sList.add(new Statement(serial+1000L,4,"if (raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() == null || (raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null && sdf.format(raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"()).compareTo(sdf.format("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"())) != 0)) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					} else if ("date".equalsIgnoreCase(f.getFieldType())){
						sList.add(new Statement(serial,3,"if ("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null) {"));
						sList.add(new Statement(serial+1000L,4,"if (raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() == null || (raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"() != null && raw"+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"().compareTo("+this.domain.getLowerFirstDomainName()+".get"+f.getCapFirstFieldName()+"()) != 0)) matches = false;"));
						sList.add(new Statement(serial+2000L,3,"}"));
					}			
					serial += 3000L;
				}
			}
			sList.add(new Statement(serial,3,"if (matches) result.add(raw"+this.domain.getLowerFirstDomainName()+");"));
			sList.add(new Statement(serial+1000L,2,"}"));
			sList.add(new Statement(serial+2000L,2,"return result;"));
			method.setMethodStatementList(WriteableUtil.merge(sList));
			return method;
		}
	}

	@Override
	public Method generateControllerMethod() throws Exception {
		return null;
	}

	@Override
	public Method generateDummyServiceImplMethod() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDaoSuffix() + "."
					+ this.domain.getStandardName() + "Dao");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getServiceSuffix() + "."
					+ this.domain.getStandardName() + "Service");
			method.addMetaData("Override");

			List<Writeable> list = new ArrayList<Writeable>();
			Method daomethod = this.generateDummyDaoMethodDefinition();
			list.add(new Statement(200L,2, "return " + daomethod.generateStandardServiceImplCallString(InterVarUtil.DB.dao.getVarName())+";"));
			method.setMethodStatementList(WriteableUtil.merge(list));
			return method;
		}
	}

	@Override
	public Method generateDummyDaoMethodDefinition() throws Exception {
		if (this.denied)
			return null;
		else {
			Method method = new Method();
			method.setStandardName("search" + StringUtil.capFirst(this.domain.getPlural()) + "ByFields");
			method.setReturnType(new Type("List", this.domain, this.domain.getPackageToken()));
			method.addAdditionalImport("java.util.List");
			method.addAdditionalImport(this.domain.getPackageToken() + "." + this.domain.getDomainSuffix() + "."
					+ this.domain.getCapFirstDomainNameWithSuffix());
			method.addSignature(new Signature(1, this.domain.getLowerFirstDomainName(), this.domain.getType()));
			method.setThrowException(true);

			return method;
		}
	}
}
